/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Url Classifier code * * The Initial Developer of the Original Code is * Google Inc. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Tony Chang <tony@ponderer.org> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const Cc = Components.classes; const Ci = Components.interfaces; /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Google Safe Browsing. * * The Initial Developer of the Original Code is Google Inc. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Niels Provos <niels@google.com> (original author) * Fritz Schneider <fritz@google.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // A class that manages lists, namely white and black lists for // phishing or malware protection. The ListManager knows how to fetch, // update, and store lists, and knows the "kind" of list each is (is // it a whitelist? a blacklist? etc). However it doesn't know how the // lists are serialized or deserialized (the wireformat classes know // this) nor the specific format of each list. For example, the list // could be a map of domains to "1" if the domain is phishy. Or it // could be a map of hosts to regular expressions to match, who knows? // Answer: the trtable knows. List are serialized/deserialized by the // wireformat reader from/to trtables, and queried by the listmanager. // // There is a single listmanager for the whole application. // // The listmanager is used only in privacy mode; in advanced protection // mode a remote server is queried. // // How to add a new table: // 1) get it up on the server // 2) add it to tablesKnown // 3) if it is not a known table type (trtable.js), add an implementation // for it in trtable.js // 4) add a check for it in the phishwarden's isXY() method, for example // isBlackURL() // // TODO: obviously the way this works could use a lot of improvement. In // particular adding a list should just be a matter of adding // its name to the listmanager and an implementation to trtable // (or not if a talbe of that type exists). The format and semantics // of the list comprise its name, so the listmanager should easily // be able to figure out what to do with what list (i.e., no // need for step 4). // TODO more comprehensive update tests, for example add unittest check // that the listmanagers tables are properly written on updates /** * The base pref name for where we keep table version numbers. * We add append the table name to this and set the value to * the version. E.g., tableversion.goog-black-enchash may have * a value of 1.1234. */ const kTableVersionPrefPrefix = "urlclassifier.tableversion."; // How frequently we check for updates (30 minutes) const kUpdateInterval = 30 * 60 * 1000; /** * A ListManager keeps track of black and white lists and knows * how to update them. * * @constructor */ function PROT_ListManager() { this.debugZone = "listmanager"; G_debugService.enableZone(this.debugZone); this.currentUpdateChecker_ = null; // set when we toggle updates this.prefs_ = new G_Preferences(); this.updateserverURL_ = null; // The lists we know about and the parses we can use to read // them. Default all to the earlies possible version (1.-1); this // version will get updated when successfully read from disk or // fetch updates. this.tablesKnown_ = {}; this.isTesting_ = false; if (this.isTesting_) { // populate with some tables for unittesting this.tablesKnown_ = { // A major version of zero means local, so don't ask for updates "test1-foo-domain" : new PROT_VersionParser("test1-foo-domain", 0, -1), "test2-foo-domain" : new PROT_VersionParser("test2-foo-domain", 0, -1), "test-white-domain" : new PROT_VersionParser("test-white-domain", 0, -1, true /* require mac*/), "test-mac-domain" : new PROT_VersionParser("test-mac-domain", 0, -1, true /* require mac */) }; // expose the object for unittesting this.wrappedJSObject = this; } this.tablesData = {}; this.observerServiceObserver_ = new G_ObserverServiceObserver( 'xpcom-shutdown', BindToObject(this.shutdown_, this), true /*only once*/); // Lazily create urlCrypto (see tr-fetcher.js) this.urlCrypto_ = null; this.requestBackoff_ = new RequestBackoff(3 /* num errors */, 10*60*1000 /* error time, 10min */, 60*60*1000 /* backoff interval, 60min */, 6*60*60*1000 /* max backoff, 6hr */); } /** * xpcom-shutdown callback * Delete all of our data tables which seem to leak otherwise. */ PROT_ListManager.prototype.shutdown_ = function() { for (var name in this.tablesData) { delete this.tablesData[name]; } } /** * Set the url we check for updates. If the new url is valid and different, * update our table list. * * After setting the update url, the caller is responsible for registering * tables and then toggling update checking. All the code for this logic is * currently in browser/components/safebrowsing. Maybe it should be part of * the listmanger? */ PROT_ListManager.prototype.setUpdateUrl = function(url) { G_Debug(this, "Set update url: " + url); if (url != this.updateserverURL_) { this.updateserverURL_ = url; this.requestBackoff_.reset(); // Remove old tables which probably aren't valid for the new provider. for (var name in this.tablesData) { delete this.tablesData[name]; delete this.tablesKnown_[name]; } } } /** * Set the crypto key url. * @param url String */ PROT_ListManager.prototype.setKeyUrl = function(url) { G_Debug(this, "Set key url: " + url); if (!this.urlCrypto_) this.urlCrypto_ = new PROT_UrlCrypto(); this.urlCrypto_.manager_.setKeyUrl(url); } /** * Register a new table table * @param tableName - the name of the table * @param opt_requireMac true if a mac is required on update, false otherwise * @returns true if the table could be created; false otherwise */ PROT_ListManager.prototype.registerTable = function(tableName, opt_requireMac) { var table = new PROT_VersionParser(tableName, 1, -1, opt_requireMac); if (!table) return false; this.tablesKnown_[tableName] = table; this.tablesData[tableName] = newUrlClassifierTable(tableName); return true; } /** * Enable updates for some tables * @param tables - an array of table names that need updating */ PROT_ListManager.prototype.enableUpdate = function(tableName) { var changed = false; var table = this.tablesKnown_[tableName]; if (table) { G_Debug(this, "Enabling table updates for " + tableName); table.needsUpdate = true; changed = true; } if (changed === true) this.maybeToggleUpdateChecking(); } /** * Disables updates for some tables * @param tables - an array of table names that no longer need updating */ PROT_ListManager.prototype.disableUpdate = function(tableName) { var changed = false; var table = this.tablesKnown_[tableName]; if (table) { G_Debug(this, "Disabling table updates for " + tableName); table.needsUpdate = false; changed = true; } if (changed === true) this.maybeToggleUpdateChecking(); } /** * Determine if we have some tables that need updating. */ PROT_ListManager.prototype.requireTableUpdates = function() { for (var type in this.tablesKnown_) { // All tables with a major of 0 are internal tables that we never // update remotely. if (this.tablesKnown_[type].major == 0) continue; // Tables that need updating even if other tables dont require it if (this.tablesKnown_[type].needsUpdate) return true; } return false; } /** * Start managing the lists we know about. We don't do this automatically * when the listmanager is instantiated because their profile directory * (where we store the lists) might not be available. */ PROT_ListManager.prototype.maybeStartManagingUpdates = function() { if (this.isTesting_) return; // We might have been told about tables already, so see if we should be // actually updating. this.maybeToggleUpdateChecking(); } /** * Determine if we have any tables that require updating. Different * Wardens may call us with new tables that need to be updated. */ PROT_ListManager.prototype.maybeToggleUpdateChecking = function() { // If we are testing or dont have an application directory yet, we should // not start reading tables from disk or schedule remote updates if (this.isTesting_) return; // We update tables if we have some tables that want updates. If there // are no tables that want to be updated - we dont need to check anything. if (this.requireTableUpdates() === true) { G_Debug(this, "Starting managing lists"); this.startUpdateChecker(); // Multiple warden can ask us to reenable updates at the same time, but we // really just need to schedule a single update. if (!this.currentUpdateChecker_) { // If the user has never downloaded tables, do the check now. // If the user has tables, add a fuzz of a few minutes. this.loadTableVersions_(); var hasTables = false; for (var table in this.tablesKnown_) { if (this.tablesKnown_[table].minor != -1) { hasTables = true; break; } } var initialUpdateDelay = 3000; if (hasTables) { // Add a fuzz of 0-5 minutes. initialUpdateDelay += Math.floor(Math.random() * (5 * 60 * 1000)); } this.currentUpdateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), initialUpdateDelay); } } else { G_Debug(this, "Stopping managing lists (if currently active)"); this.stopUpdateChecker(); // Cancel pending updates } } /** * Start periodic checks for updates. Idempotent. * We want to distribute update checks evenly across the update period (an * hour). To do this, we pick a random number of time between 0 and 30 * minutes. The client first checks at 15 + rand, then every 30 minutes after * that. */ PROT_ListManager.prototype.startUpdateChecker = function() { this.stopUpdateChecker(); // Schedule the first check for between 15 and 45 minutes. var repeatingUpdateDelay = kUpdateInterval / 2; repeatingUpdateDelay += Math.floor(Math.random() * kUpdateInterval); this.updateChecker_ = new G_Alarm(BindToObject(this.initialUpdateCheck_, this), repeatingUpdateDelay); } /** * Callback for the first update check. * We go ahead and check for table updates, then start a regular timer (once * every 30 minutes). */ PROT_ListManager.prototype.initialUpdateCheck_ = function() { this.checkForUpdates(); this.updateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), kUpdateInterval, true /* repeat */); } /** * Stop checking for updates. Idempotent. */ PROT_ListManager.prototype.stopUpdateChecker = function() { if (this.updateChecker_) { this.updateChecker_.cancel(); this.updateChecker_ = null; } // Cancel the oneoff check from maybeToggleUpdateChecking. if (this.currentUpdateChecker_) { this.currentUpdateChecker_.cancel(); this.currentUpdateChecker_ = null; } } /** * Provides an exception free way to look up the data in a table. We * use this because at certain points our tables might not be loaded, * and querying them could throw. * * @param table String Name of the table that we want to consult * @param key String Key for table lookup * @param callback nsIUrlListManagerCallback (ie., Function) given false or the * value in the table corresponding to key. If the table name does not * exist, we return false, too. */ PROT_ListManager.prototype.safeExists = function(table, key, callback) { try { G_Debug(this, "safeExists: " + table + ", " + key); var map = this.tablesData[table]; map.exists(key, callback); } catch(e) { G_Debug(this, "safeExists masked failure for " + table + ", key " + key + ": " + e); callback.handleEvent(false); } } /** * We store table versions in user prefs. This method pulls the values out of * the user prefs and into the tablesKnown objects. */ PROT_ListManager.prototype.loadTableVersions_ = function() { // Pull values out of prefs. var prefBase = kTableVersionPrefPrefix; for (var table in this.tablesKnown_) { var version = this.prefs_.getPref(prefBase + table, "1.-1"); G_Debug(this, "loadTableVersion " + table + ": " + version); var tokens = version.split("."); G_Assert(this, tokens.length == 2, "invalid version number"); this.tablesKnown_[table].major = tokens[0]; this.tablesKnown_[table].minor = tokens[1]; } } /** * Callback from db update service. As new tables are added to the db, * this callback is fired so we can update the version number. * @param versionString String containing the table update response from the * server */ PROT_ListManager.prototype.setTableVersion_ = function(versionString) { G_Debug(this, "Got version string: " + versionString); var versionParser = new PROT_VersionParser(""); if (versionParser.fromString(versionString)) { var tableName = versionParser.type; var versionNumber = versionParser.versionString(); var prefBase = kTableVersionPrefPrefix; this.prefs_.setPref(prefBase + tableName, versionNumber); if (!this.tablesKnown_[tableName]) { this.tablesKnown_[tableName] = versionParser; } else { this.tablesKnown_[tableName].ImportVersion(versionParser); } if (!this.tablesData[tableName]) this.tablesData[tableName] = newUrlClassifierTable(tableName); } // Since this is called from the update server, it means there was // a successful http request. Make sure to notify the request backoff // object. this.requestBackoff_.noteServerResponse(200 /* ok */); } /** * Prepares a URL to fetch upates from. Format is a squence of * type:major:minor, fields * * @param url The base URL to which query parameters are appended; assumes * already has a trailing ? * @returns the URL that we should request the table update from. */ PROT_ListManager.prototype.getRequestURL_ = function(url) { url += "version="; var firstElement = true; var requestMac = false; for (var type in this.tablesKnown_) { // All tables with a major of 0 are internal tables that we never // update remotely. if (this.tablesKnown_[type].major == 0) continue; // Check if the table needs updating if (this.tablesKnown_[type].needsUpdate == false) continue; if (!firstElement) { url += "," } else { firstElement = false; } url += type + ":" + this.tablesKnown_[type].toUrl(); if (this.tablesKnown_[type].requireMac) requestMac = true; } // Request a mac only if at least one of the tables to be updated requires // it if (requestMac) { // Add the wrapped key for requesting macs if (!this.urlCrypto_) this.urlCrypto_ = new PROT_UrlCrypto(); url += "&wrkey=" + encodeURIComponent(this.urlCrypto_.getManager().getWrappedKey()); } G_Debug(this, "getRequestURL returning: " + url); return url; } /** * Updates our internal tables from the update server * * @returns true when a new request was scheduled, false if an old request * was still pending. */ PROT_ListManager.prototype.checkForUpdates = function() { // Allow new updates to be scheduled from maybeToggleUpdateChecking() this.currentUpdateChecker_ = null; if (!this.updateserverURL_) { G_Debug(this, 'checkForUpdates: no update server url'); return false; } // See if we've triggered the request backoff logic. if (!this.requestBackoff_.canMakeRequest()) return false; // Check to make sure our tables still exist (maybe the db got corrupted or // the user deleted the file). If not, we need to reset the table version // before sending the update check. var tableNames = []; for (var tableName in this.tablesKnown_) { tableNames.push(tableName); } var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"] .getService(Ci.nsIUrlClassifierDBService); dbService.checkTables(tableNames.join(","), BindToObject(this.makeUpdateRequest_, this)); return true; } /** * Method that fires the actual HTTP update request. * First we reset any tables that have disappeared. * @param tableNames String comma separated list of tables that * don't exist */ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableNames) { // Clear prefs that track table version if they no longer exist in the db. var tables = tableNames.split(","); for (var i = 0; i < tables.length; ++i) { G_Debug(this, "Table |" + tables[i] + "| no longer exists, clearing pref."); this.prefs_.clearPref(kTableVersionPrefPrefix + tables[i]); } // Ok, now reload the table version. this.loadTableVersions_(); G_Debug(this, 'checkForUpdates: scheduling request..'); var url = this.getRequestURL_(this.updateserverURL_); var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"] .getService(Ci.nsIUrlClassifierStreamUpdater); try { streamer.updateUrl = url; } catch (e) { G_Debug(this, 'invalid url'); return; } if (!streamer.downloadUpdates(BindToObject(this.setTableVersion_, this), BindToObject(this.downloadError_, this))) { G_Debug(this, "pending update, wait until later"); } } /** * Callback function if there's a download error. * @param status String http status or an empty string if connection refused. */ PROT_ListManager.prototype.downloadError_ = function(status) { G_Debug(this, "download error: " + status); // If status is empty, then we assume that we got an NS_CONNECTION_REFUSED // error. In this case, we treat this is a http 500 error. if (!status) { status = 500; } status = parseInt(status, 10); this.requestBackoff_.noteServerResponse(status); // Try again in a minute this.currentUpdateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), 60000); } PROT_ListManager.prototype.QueryInterface = function(iid) { if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIUrlListManager) || iid.equals(Ci.nsITimerCallback)) return this; Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE; return null; } // A simple factory function that creates nsIUrlClassifierTable instances based // on a name. The name is a string of the format // provider_name-semantic_type-table_type. For example, goog-white-enchash // or goog-black-url. function newUrlClassifierTable(name) { G_Debug("protfactory", "Creating a new nsIUrlClassifierTable: " + name); var tokens = name.split('-'); var type = tokens[2]; var table = Cc['@mozilla.org/url-classifier/table;1?type=' + type] .createInstance(Ci.nsIUrlClassifierTable); table.name = name; return table; } /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Google Safe Browsing. * * The Initial Developer of the Original Code is Google Inc. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Niels Provos <niels@google.com> (original author) * Fritz Schneider <fritz@google.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // A class that serializes and deserializes opaque key/value string to // string maps to/from maps (trtables). It knows how to create // trtables from the serialized format, so it also understands // meta-information like the name of the table and the table's // version. See docs for the protocol description. // // TODO: wireformatreader: if you have multiple updates for one table // in a call to deserialize, the later ones will be merged // (all but the last will be ignored). To fix, merge instead // of replace when you have an existing table, and only do so once. // TODO must have blank line between successive types -- problem? // TODO doesn't tolerate blank lines very well // // Maybe: These classes could use a LOT more cleanup, but it's not a // priority at the moment. For example, the tablesData/Known // maps should be combined into a single object, the parser // for a given type should be separate from the version info, // and there should be synchronous interfaces for testing. /** * A class that knows how to serialize and deserialize meta-information. * This meta information is the table name and version number, and * in its serialized form looks like the first line below: * * [name-of-table X.Y update?] * ...key/value pairs to add or delete follow... * <blank line ends the table> * * The X.Y is the version number and the optional "update" token means * that the table is a differential from the curent table the extension * has. Its absence means that this is a full, new table. */ function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) { this.debugZone = "versionparser"; this.type = type; this.major = 0; this.minor = 0; this.badHeader = false; // Should the wireformatreader compute a mac? this.mac = false; this.macval = ""; this.macFailed = false; this.requireMac = !!opt_requireMac; this.update = false; this.needsUpdate = false; // used by ListManager to determine update policy // Used by ListerManager to see if we have read data for this table from // disk. Once we read a table from disk, we are not going to do so again // but instead update remotely if necessary. this.didRead = false; if (opt_major) this.major = parseInt(opt_major); if (opt_minor) this.minor = parseInt(opt_minor); } /** Import the version information from another VersionParser * @params version a version parser object */ PROT_VersionParser.prototype.ImportVersion = function(version) { this.major = version.major; this.minor = version.minor; this.mac = version.mac; this.macFailed = version.macFailed; this.macval = version.macval; // Don't set requireMac, since we create vparsers from scratch and doesn't // know about it } /** * Creates a string like [goog-white-black 1.1] from internal information * * @returns String */ PROT_VersionParser.prototype.toString = function() { var s = "[" + this.type + " " + this.major + "." + this.minor + "]"; return s; } /** * Creates a string like 1.123 with the version number. This is the * format we store in prefs. * @return String */ PROT_VersionParser.prototype.versionString = function() { return this.major + "." + this.minor; } /** * Creates a string like 1:1 from internal information used for * fetching updates from the server. Called by the listmanager. * * @returns String */ PROT_VersionParser.prototype.toUrl = function() { return this.major + ":" + this.minor; } /** * Process the old format, [type major.minor [update]] * * @returns true if the string could be parsed, false otherwise */ PROT_VersionParser.prototype.processOldFormat_ = function(line) { if (line[0] != '[' || line.slice(-1) != ']') return false; var description = line.slice(1, -1); // Get the type name and version number of this table var tokens = description.split(" "); this.type = tokens[0]; var majorminor = tokens[1].split("."); this.major = parseInt(majorminor[0]); this.minor = parseInt(majorminor[1]); if (isNaN(this.major) || isNaN(this.minor)) return false; if (tokens.length >= 3) { this.update = tokens[2] == "update"; } return true; } /** * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the * type and corresponding version numbers. * @returns true if the string could be parsed, false otherwise */ PROT_VersionParser.prototype.fromString = function(line) { G_Debug(this, "Calling fromString with line: " + line); if (line[0] != '[' || line.slice(-1) != ']') return false; // There could be two [][], so take care of it var secondBracket = line.indexOf('[', 1); var firstPart = null; var secondPart = null; if (secondBracket != -1) { firstPart = line.substring(0, secondBracket); secondPart = line.substring(secondBracket); G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart); } else { firstPart = line; G_Debug(this, "Old format: " + firstPart); } if (!this.processOldFormat_(firstPart)) return false; if (secondPart && !this.processOptTokens_(secondPart)) return false; return true; } /** * Process optional tokens * * @param line A string [token1=val1 token2=val2...] * @returns true if the string could be parsed, false otherwise */ PROT_VersionParser.prototype.processOptTokens_ = function(line) { if (line[0] != '[' || line.slice(-1) != ']') return false; var description = line.slice(1, -1); // Get the type name and version number of this table var tokens = description.split(" "); for (var i = 0; i < tokens.length; i++) { G_Debug(this, "Processing optional token: " + tokens[i]); var tokenparts = tokens[i].split("="); switch(tokenparts[0]){ case "mac": this.mac = true; if (tokenparts.length < 2) { G_Debug(this, "Found mac flag but not mac value!"); return false; } // The mac value may have "=" in it, so we can't just use tokenparts[1]. // Instead, just take the rest of tokens[i] after the first "=" this.macval = tokens[i].substr(tokens[i].indexOf("=")+1); break; default: G_Debug(this, "Found unrecognized token: " + tokenparts[0]); break; } } return true; } //@line 43 "/cygdrive/c/builds/tinderbox/Fx-Mozilla1.8-release/WINNT_5.2_Depend/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierListManager.js" var modScope = this; function Init() { // Pull the library in. var jslib = Cc["@mozilla.org/url-classifier/jslib;1"] .getService().wrappedJSObject; Function.prototype.inherits = jslib.Function.prototype.inherits; modScope.G_Preferences = jslib.G_Preferences; modScope.G_PreferenceObserver = jslib.G_PreferenceObserver; modScope.G_ObserverServiceObserver = jslib.G_ObserverServiceObserver; modScope.G_Debug = jslib.G_Debug; modScope.G_Assert = jslib.G_Assert; modScope.G_debugService = jslib.G_debugService; modScope.G_Alarm = jslib.G_Alarm; modScope.BindToObject = jslib.BindToObject; modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher; modScope.PROT_UrlCrypto = jslib.PROT_UrlCrypto; modScope.RequestBackoff = jslib.RequestBackoff; // We only need to call Init once. modScope.Init = function() {}; } // Module object function UrlClassifierListManagerMod() { this.firstTime = true; this.cid = Components.ID("{ca168834-cc00-48f9-b83c-fd018e58cae3}"); this.progid = "@mozilla.org/url-classifier/listmanager;1"; } UrlClassifierListManagerMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) { if (this.firstTime) { this.firstTime = false; throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN; } compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); compMgr.registerFactoryLocation(this.cid, "UrlClassifier List Manager Module", this.progid, fileSpec, loc, type); }; UrlClassifierListManagerMod.prototype.getClassObject = function(compMgr, cid, iid) { if (!cid.equals(this.cid)) throw Components.results.NS_ERROR_NO_INTERFACE; if (!iid.equals(Ci.nsIFactory)) throw Components.results.NS_ERROR_NOT_IMPLEMENTED; return this.factory; } UrlClassifierListManagerMod.prototype.canUnload = function(compMgr) { return true; } UrlClassifierListManagerMod.prototype.factory = { createInstance: function(outer, iid) { if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; Init(); return (new PROT_ListManager()).QueryInterface(iid); } }; var ListManagerModInst = new UrlClassifierListManagerMod(); function NSGetModule(compMgr, fileSpec) { return ListManagerModInst; }